/*
 * Collections-C
 * Copyright (C) 2013-2015 Srđan Panić <srdja.panic@gmail.com>
 *
 * This file is part of Collections-C.
 *
 * Collections-C is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Collections-C is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Collections-C.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef COLLECTIONS_C_ARRAY_H
#define COLLECTIONS_C_ARRAY_H

#include "common.h"

/**
 * A dynamic array that expands automatically as elements are
 * added. The array supports amortized constant time insertion
 * and removal of elements at the end of the array, as well as
 * constant time access.
 */
typedef struct array_s Array;

/**
 * Array configuration structure. Used to initialize a new Array
 * with specific values.
 */
typedef struct array_conf_s {
    /**
     * The initial capacity of the array */
    size_t capacity;

    /**
     * The rate at which the buffer expands (capacity * exp_factor). */
    float exp_factor;

    /**
     * Memory allocators used to allocate the Array structure and the
     * underlying data buffers. */
    void *(*mem_alloc)(size_t size);

    void *(*mem_calloc)(size_t blocks, size_t size);

    void (*mem_free)(void *block);
} ArrayConf;

/**
 * Array iterator structure. Used to iterate over the elements of
 * the array in an ascending order. The iterator also supports
 * operations for safely adding and removing elements during
 * iteration.
 */
typedef struct array_iter_s {
    /**
     * The array associated with this iterator */
    Array *ar;

    /**
     * The current position of the iterator.*/
    size_t index;

    /**
     * Set to true if the last returned element was removed. */
    bool last_removed;
} ArrayIter;

/**
 * Array zip iterator structure. Used to iterate over the elements of two
 * arrays in lockstep in an ascending order until one of the Arrays is
 * exhausted. The iterator also supports operations for safely adding
 * and removing elements during iteration.
 */
typedef struct array_zip_iter_s {
    Array *ar1;
    Array *ar2;
    size_t index;
    bool last_removed;
} ArrayZipIter;


enum cc_stat array_new(Array **out);

enum cc_stat array_new_conf(ArrayConf const *const conf, Array **out);

void array_conf_init(ArrayConf *conf);

void array_destroy(Array *ar);

void array_destroy_cb(Array *ar, void (*cb)(void *));

enum cc_stat array_add(Array *ar, void *element);

enum cc_stat array_add_at(Array *ar, void *element, size_t index);

enum cc_stat array_replace_at(Array *ar, void *element, size_t index, void **out);

enum cc_stat array_swap_at(Array *ar, size_t index1, size_t index2);

enum cc_stat array_remove(Array *ar, void *element, void **out);

enum cc_stat array_remove_at(Array *ar, size_t index, void **out);

enum cc_stat array_remove_last(Array *ar, void **out);

void array_remove_all(Array *ar);

void array_remove_all_free(Array *ar);

enum cc_stat array_get_at(Array *ar, size_t index, void **out);

enum cc_stat array_get_last(Array *ar, void **out);

enum cc_stat array_subarray(Array *ar, size_t from, size_t to, Array **out);

enum cc_stat array_copy_shallow(Array *ar, Array **out);

enum cc_stat array_copy_deep(Array *ar, void *(*cp)(void *), Array **out);

void array_reverse(Array *ar);

enum cc_stat array_trim_capacity(Array *ar);

size_t array_contains(Array *ar, void *element);

size_t array_contains_value(Array *ar, void *element, int (*cmp)(const void *, const void *));

size_t array_size(Array *ar);

size_t array_capacity(Array *ar);

enum cc_stat array_index_of(Array *ar, void *element, size_t *index);

void array_sort(Array *ar, int (*cmp)(const void *, const void *));

void array_map(Array *ar, void (*fn)(void *));

void array_reduce(Array *ar, void (*fn)(void *, void *, void *), void *result);

enum cc_stat array_filter_mut(Array *ar, bool (*predicate)(const void *));

enum cc_stat array_filter(Array *ar, bool (*predicate)(const void *), Array **out);

void array_iter_init(ArrayIter *iter, Array *ar);

enum cc_stat array_iter_next(ArrayIter *iter, void **out);

enum cc_stat array_iter_remove(ArrayIter *iter, void **out);

enum cc_stat array_iter_add(ArrayIter *iter, void *element);

enum cc_stat array_iter_replace(ArrayIter *iter, void *element, void **out);

size_t array_iter_index(ArrayIter *iter);


void array_zip_iter_init(ArrayZipIter *iter, Array *a1, Array *a2);

enum cc_stat array_zip_iter_next(ArrayZipIter *iter, void **out1, void **out2);

enum cc_stat array_zip_iter_add(ArrayZipIter *iter, void *e1, void *e2);

enum cc_stat array_zip_iter_remove(ArrayZipIter *iter, void **out1, void **out2);

enum cc_stat array_zip_iter_replace(ArrayZipIter *iter, void *e1, void *e2, void **out1, void **out2);

size_t array_zip_iter_index(ArrayZipIter *iter);

const void *const *array_get_buffer(Array *ar);


#define ARRAY_FOREACH(val, array, body)         \
    {                                           \
        ArrayIter array_iter_53d46d2a04458e7b;  \
        array_iter_init(&array_iter_53d46d2a04458e7b, array);   \
        void *val;                                              \
        while (array_iter_next(&array_iter_53d46d2a04458e7b, &val) != CC_ITER_END) \
            body                                                        \
                }


#define ARRAY_FOREACH_ZIP(val1, val2, array1, array2, body)     \
    {                                                           \
        ArrayZipIter array_zip_iter_ea08d3e52f25883b3;                 \
        array_zip_iter_init(&array_zip_iter_ea08d3e52f25883b, array1, array2); \
        void *val1;                                                     \
        void *val2;                                                     \
        while (array_zip_iter_next(&array_zip_iter_ea08d3e52f25883b3, &val1, &val2) != CC_ITER_END) \
            body                                                        \
                }

#endif /* COLLECTIONS_C_ARRAY_H */
